import { createStore } from '@mpxjs/core'
import Tim from '../../lib/sdk/tim'
import Cos from 'cos-wx-sdk-v5'

import TYPES from '../util/types'
import { isJSON, formatTime } from '../util'
import { decodeElement } from '../util/decodeElement'

/**
 * 判断是否是对象
 * @param value
 * @returns {boolean|boolean}
 */
function isObject (value) {
  const type = typeof value
  return value != null && (type === 'object' || type === 'function')
}

/**
 * 处理消息
 * @param messages
 */
function handleMessages (messages) {
  for (let i = 0; i < messages.length; i++) {
    const message = messages[i]
    messages[i].virtualDom = decodeElement(message)
    const date = new Date(message.time * 1000)
    messages[i].newtime = formatTime(date)
  }
}

// 获取随机数
function getRandomInt (min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)
  return Math.floor(Math.random() * (max - min)) + min
}

/**
 * 处理视频消息
 * @param message
 * @param data
 * @param getters
 */
function handleVideoChatCustomMessage (message, data, getters) {
  switch (data.action) {
    // 对方呼叫我
    case 0:
      if (!getters.isCalling) {
        wx.navigateTo({
          url: `/im/pages/call/index?args=${message.payload.data}&from=${message.from}&to=${message.to}`
        })
      } else {
        // 触发事件
        getters.bus.emit('isCalling', message)
      }
      break
    // 对方取消
    case 1:
      getters.bus.emit('onCancel')
      break
    // 对方拒绝
    case 2:
      // 发送事件
      getters.bus.emit('onRefuse')
      break
    // 对方不接1min
    case 3:
      wx.navigateBack({
        delta: 1
      })
      break
    // 对方接听
    case 4:
      // 发送事件
      getters.bus.emit('onCall')
      break
    // 对方挂断
    case 5:
      // 发送事件
      getters.bus.emit('onClose')
      break
    // 对方正在通话中
    case 6:
      // 发送事件
      getters.bus.emit('onBusy')
      break
    default:
      break
  }
}

// 自定义bus事件驱动
class Bus {
  // 监听者（在这里是函数）
  listeners = {}
  // 监听事件
  on (eventName, cb = () => {}) {
    if (this.listeners.hasOwnProperty(eventName)) {
      // 添加监听者
      this.listeners[eventName].push(cb)
      return
    }
    this.listeners[eventName] = [ cb ]
  }
  // 发布事件
  emit (eventName, args) {
    if (this.listeners.hasOwnProperty(eventName)) {
      this.listeners[eventName].forEach(listener => listener(args))
    }
  }
  // 取消监听事件
  off (eventName, cb) {
    if (this.listeners.hasOwnProperty(eventName)) {
      if (typeof cb === 'function') {
        this.listeners[eventName].splice(this.listeners[eventName].findIndex(listener => listener === cb), 1)
        return
      }
      // 清理掉所有监听者
      delete this.listeners[eventName]
    }
  }
}

export default createStore({
  state: {
    // 事件驱动bus
    bus: new Bus(),
    // tim实例
    tim: null,
    // rtc sdk 的 appId
    rtcSdkAppId: process.env.RTC_SDK_APP_ID,
    // sdk的appId
    sdkAppId: process.env.SDK_APP_ID,
    // rtc用户sig
    rtcUserSig: '',
    // sdk日志输出级别
    logLevel: process.env.TIM_LOG_LEVEL,
    // sdk是否准备就绪
    sdkReady: false,
    // 用户信息
    userInfo: {},
    // 对方ID
    toId: '',
    // 是否正在通话
    calling: false,
    // 当前会话对象
    conversation: {},
    // 当前的会话ID
    conversationId: null,
    // 会话类型
    conversationType: null,
    // 下一次拉取消息的ID
    nextMessageId: '',
    // 是否正在拉取消息
    isLoading: false,
    // 是否拉取完成
    isCompleted: false,
    // 当前会话消息
    currentMessages: [],
    // 底部安全距离
    safeBottom: 0
  },
  getters: {
    bus (state) {
      return state.bus
    },
    tim (state) {
      return state.tim
    },
    rtcSdkAppId (state) {
      return state.rtcSdkAppId
    },
    sdkAppId (state) {
      return state.sdkAppId
    },
    rtcUserSig (state) {
      return state.rtcUserSig
    },
    logLevel (state) {
      return state.logLevel
    },
    userInfo (state) {
      return state.userInfo
    },
    toId (state) {
      return state.toId
    },
    isSdkReady (state) {
      return state.sdkReady
    },
    isCalling (state) {
      return state.calling
    },
    conversation (state) {
      return state.conversation
    },
    conversationId (state) {
      return state.conversationId
    },
    conversationType (state) {
      return state.conversationType
    },
    currentMessages (state) {
      return state.currentMessages
    },
    isLoading (state) {
      return state.isLoading
    },
    nextMessageId (state) {
      return state.nextMessageId
    },
    isCompleted (state) {
      return state.isCompleted
    },
    safeBottom (state) {
      return state.safeBottom
    }
  },
  mutations: {
    setTim (state, tim) {
      state.tim = tim
    },
    setSdkReady (state, status) {
      state.sdkReady = status
    },
    setRtcUserSig (state, sig) {
      state.rtcUserSig = sig
    },
    setUserInfo (state, userInfo) {
      state.userInfo = userInfo
    },
    setToId (state, toId) {
      state.toId = toId
    },
    setCalling (state, status) {
      state.calling = status
    },
    setNextMessageId (state, id) {
      state.nextMessageId = id
    },
    setLoading (state, status) {
      state.isLoading = status
    },
    setCompleted (state, status) {
      state.isCompleted = status
    },
    // 设置底部安全距离
    setSafeBottom (state, value) {
      state.safeBottom = value
    },
    // 历史头插消息列表
    unshiftMessages (state, messages) {
      const newMessages = [...messages]
      handleMessages(newMessages)
      state.currentMessages = [...newMessages, ...state.currentMessages]
    },
    // 接收消息
    receiveMessages (state, messages) {
      const newMessages = [...messages]
      handleMessages(newMessages)
      state.currentMessages = [...state.currentMessages, ...newMessages]
    },
    // 发送消息
    sendMessage (state, message) {
      message.virtualDom = decodeElement(message)
      const date = new Date(message.time * 1000)
      message.newtime = formatTime(date)
      state.currentMessages.push(message)
      if (!state.conversationId) {
        // 第一次会话
        state.isCompleted = true
      }
    },
    // 删除消息
    removeMessage (state, message) {
      state.currentMessages.splice(state.currentMessages.findIndex(item => item.ID === message.ID), 1)
    },
    // 改变消息状态
    changeMessageStatus (state, { index, status, message }) {
      if (isObject(message)) {
        state.currentMessages[index] = {
          ...state.currentMessages[index],
          ...message
        }
        return
      }
      state.currentMessages[index] = {
        ...state.currentMessages[index],
        status
      }
    },
    // 更新会话
    updateConversation (state, conversation) {
      state.conversation = conversation
      // 设置标题
      wx.setNavigationBarTitle({
        title: conversation.type === TYPES.CONV_C2C ? (conversation.userProfile.nick || conversation.userProfile.userID) : (conversation.groupProfile.name || conversation.groupProfile.groupID)
      })
    },
    // 设置会话ID
    setConversationId (state, id) {
      state.conversationId = id
    },
    // 设置会话类型
    setConversationType (state, type) {
      state.conversationType = type
    },
    // 更新会话对方信息
    setConversationUserProfile (state, profile) {
      state.conversation = {
        ...state.conversation,
        userProfile: profile
      }
    },
    // 重置会话
    resetConversation (state) {
      state.conversationId = '' // 当前聊天对话ID
      state.conversation = {} // 当前聊天对话信息
      state.currentMessages = [] // 当前聊天消息列表
      state.nextMessageId = '' // 下一条消息标志
      state.isCompleted = false // 当前会话消息是否已经请求完毕
      state.isLoading = false // 是否正在请求
    }
  },
  actions: {
    initTim ({ getters, commit, dispatch }) {
      if (isObject(getters.tim)) {
        // 已经初始化过了，直接返回
        return
      }
      wx.showLoading({
        title: '加载中',
        mask: true
      })
      // 初始化TIM
      const tim = Tim.create({
        SDKAppID: getters.sdkAppId
      })
      // 保存tim对象
      commit('setTim', tim)
      // 设置 SDK 日志输出级别
      // 普通级别，日志量较多，接入时建议使用
      // release级别，SDK 输出关键信息，生产环境时建议使用
      tim.setLogLevel(getters.logLevel)

      // 将腾讯云对象存储服务 SDK （以下简称 COS SDK）注册为插件，IM SDK 发送文件、图片等消息时，需要用到腾讯云的 COS 服务
      // 小程序 环境，注册 COS SDK
      tim.registerPlugin({ 'cos-wx-sdk': Cos })

      // 监听事件
      tim.on(Tim.EVENT.SDK_READY, function (event) {
        // 收到离线消息和会话列表同步完毕通知，接入侧可以调用 sendMessage 等需要鉴权的接口
        // event.name - Tim.EVENT.SDK_READY
        tim.getMyProfile().then(res => {
          commit('setUserInfo', res.data)
        })
        commit('setSdkReady', true)
        // 获取一次消息
        setTimeout(() => {
          dispatch('getMessages')
        }, 800)
      })

      tim.on(Tim.EVENT.SDK_NOT_READY, function (event) {
        // 收到 SDK 进入 not ready 状态通知，此时 SDK 无法正常工作
        // event.name - Tim.EVENT.SDK_NOT_READY
        commit('setSdkReady', false)
      })

      tim.on(Tim.EVENT.PROFILE_UPDATED, function (event) {
        // 收到自己或好友的资料变更通知
        // event.name - TIM.EVENT.PROFILE_UPDATED
        // event.data - 存储 Profile 对象的数组 - [Profile]
        event.data.forEach(profile => {
          if (profile.userID === getters.userInfo.userID) {
            // 更新信息
            commit('setUserInfo', profile)
          } else if (profile.userID === getters.conversation.userProfile.userID) {
            commit('setConversationUserProfile', profile)
          }
        })
      })

      tim.on(Tim.EVENT.CONVERSATION_LIST_UPDATED, function (event) {
        // 收到会话列表更新通知，可通过遍历 event.data 获取会话列表数据并渲染到页面
        // event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
        // event.data - 存储 Conversation 对象的数组 - [Conversation]
        event.data.forEach(conversation => {
          if (conversation.conversationID === getters.conversationId) {
            commit('updateConversation', conversation)
          }
        })
      })

      tim.on(Tim.EVENT.MESSAGE_RECEIVED, function (event) {
        // 收到推送的单聊、群聊、群提示、群系统通知的新消息，可通过遍历 event.data 获取消息列表数据并渲染到页面
        // event.name - Tim.EVENT.MESSAGE_RECEIVED
        // event.data - 存储 Message 对象的数组 - [Message]
        for (let i = 0; i < event.data.length; i++) {
          const message = event.data[i]
          if (message.type === TYPES.MSG_CUSTOM) {
            if (isJSON(message.payload.data)) {
              const data = JSON.parse(message.payload.data)
              if (data.customType === 'videoChat') {
                // 视频聊天
                handleVideoChatCustomMessage(message, data, getters)
              }
            }
          }
        }
        dispatch('onMessageEvent', event)
      })

      tim.on(Tim.EVENT.MESSAGE_REVOKED, function (event) {
        // 收到消息被撤回的通知。使用前需要将SDK版本升级至v2.4.0或以上。
        // event.name - Tim.EVENT.MESSAGE_REVOKED
        // event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isRevoked 属性值为 true
      })

      tim.on(Tim.EVENT.KICKED_OUT, function (event) {
        // 收到被踢下线通知
        // event.name - TIM.EVENT.KICKED_OUT
        // event.data.type - 被踢下线的原因，例如 :
        //    - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多实例登录被踢
        //    - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多终端登录被踢
        //    - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 签名过期被踢。使用前需要将SDK版本升级至v2.4.0或以上。
        let message = '强迫下线'
        switch (event.data.type) {
          case Tim.TYPES.KICKED_OUT_MULT_ACCOUNT:
            message = '账号在别的地方登录'
            break
          case Tim.KICKED_OUT_MULT_DEVICE:
            message = '账号在其他终端登录'
            break
          case Tim.TYPES.KICKED_OUT_USERSIG_EXPIRED:
            message = '账号签名秘钥过期'
        }
        wx.redirectTo({
          url: '/pages/index/index',
          success () {
            wx.showToast({
              title: message,
              icon: 'none',
              duration: 3000
            })
          }
        })
      })

      tim.on(Tim.EVENT.ERROR, function (event) {
        // 收到 SDK 发生错误通知，可以获取错误码和错误信息
        // event.name - Tim.EVENT.ERROR
        // event.data.code - 错误码
        // event.data.message - 错误信息
        console.log(event)
      })

      // 判断手机
      const systemInfo = wx.getSystemInfoSync()
      // 设置底部安全高度
      commit('setSafeBottom', systemInfo.screenHeight - systemInfo.safeArea.bottom)

      wx.hideLoading()
    },
    // 消息事件
    onMessageEvent ({ getters, commit }, event) {
      if (event.name === Tim.EVENT.MESSAGE_RECEIVED) {
        let id = getters.conversationId
        // 过滤非当前会话的消息
        if (!id) {
          return
        }
        let messages = []
        event.data.forEach(item => {
          if (item.conversationID === id) {
            messages.push(item)
          }
        })
        commit('receiveMessages', messages)
      }
    },
    // 真正发送消息逻辑
    handleSendMessage ({ getters, commit }, { message, cb = () => {} }) {
      // 消息即将在消息数组中的位置
      const index = getters.currentMessages.length
      // 发送消息
      commit('sendMessage', message)
      // 滚动到最底部
      // 调用SDK发送消息
      getters.tim.sendMessage(message).then(({ data: { message } }) => {
        commit('changeMessageStatus', {
          index,
          status: 'success',
          message
        })
        cb()
      }).catch(() => {
        // 消息发送失败，改变消息状态
        commit('changeMessageStatus', {
          index,
          status: 'fail'
        })
      })
    },
    // 拉取消息
    getMessages ({ getters, commit }) {
      if (!getters.conversationId) {
        return
      }
      // 判断是否拉完了
      if (!getters.isCompleted) {
        if (!getters.isLoading) {
          // 设置加载状态
          commit('setLoading', true)
          getters.tim.getMessageList({ conversationID: getters.conversationId, nextReqMessageID: getters.nextMessageId, count: 15 }).then(res => {
            commit('setNextMessageId', res.data.nextReqMessageID)
            commit('unshiftMessages', res.data.messageList)
            if (res.data.isCompleted) {
              // 加载完成
              commit('setCompleted', true)
            }
            commit('setLoading', false)
          }).catch(err => {
            console.log(err)
            commit('setLoading', false)
          })
        } else {
          wx.showToast({
            title: '你拉的太快了',
            icon: 'none',
            duration: 500
          })
        }
      } else {
        wx.showToast({
          title: '没有更多啦',
          icon: 'none',
          duration: 1500
        })
      }
    },
    // 切换到会话
    checkoutConversation ({ getters, commit, dispatch }, conversationID) {
      // 重置会话
      commit('resetConversation')
      getters.tim.setMessageRead({ conversationID })
      getters.tim.getConversationProfile(conversationID)
        .then(({ data: { conversation } }) => {
          commit('updateConversation', conversation)
          dispatch('getMessages')
          wx.navigateTo({
            url: `/im/pages/chat/index`
          })
        })
    },
    // 视频会话
    callVideo ({ getters, dispatch }, userId) {
      const options = {
        customType: 'videoChat',
        roomId: getRandomInt(1, 99999999),
        action: 0,
        duration: 0
      }
      // 传递的参数
      const data = JSON.stringify(options)
      const message = getters.tim.createCustomMessage({
        to: userId,
        conversationType: TYPES.CONV_C2C,
        payload: {
          data,
          description: '',
          extension: ''
        }
      })
      // 发送消息
      dispatch('handleSendMessage', {
        message
      })
      return Promise.resolve({
        data,
        message
      })
    },
    // 群视频会话
    callMultiVideo ({ getters, dispatch }, groupId) {
      const options = {
        customType: 'multiVideoChat',
        roomId: getRandomInt(1, 99999999)
      }
      // 传递的参数
      const data = JSON.stringify(options)
      const message = getters.tim.createCustomMessage({
        to: groupId,
        conversationType: TYPES.CONV_GROUP,
        payload: {
          data,
          description: '',
          extension: ''
        }
      })
      // 发送消息
      dispatch('handleSendMessage', {
        message
      })
      wx.setStorageSync('roomId', `${options.roomId}`)
      // 跳转视频页面
      wx.navigateTo({
        url: `/im/pages/room/index`
      })
    }
  }

})
